namespace SubSonic
{
    using SubSonic.Utilities;
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Data;
    using System.IO;
    using System.Text;
    using System.Web;
    using System.Web.UI.WebControls;
    using System.Xml.Serialization;

    [Serializable]
    public abstract class AbstractRecord<T> where T: AbstractRecord<T>, new()
    {
        private bool _isLoaded;
        internal bool _isNew;
        private string _providerName;
        private TableSchema.TableColumnSettingCollection columnSettings;
        private readonly List<string> errorList;
        private string invalidTypeExceptionMessage;
        private string lengthExceptionMessage;
        private string nullExceptionMessage;
        protected static TableSchema.Table table;
        private string tableName;

        protected AbstractRecord()
        {
            this._isNew = true;
            this.nullExceptionMessage = "{0} requires a value";
            this.invalidTypeExceptionMessage = "{0} is not a valid {1}";
            this.lengthExceptionMessage = "{0} exceeds the maximum length of {1}";
            this.errorList = new List<string>();
        }

        internal static void CheckLogicalDelete(SubSonic.Query q)
        {
            q.CheckLogicalDelete();
        }

        public T Clone()
        {
            T local = Activator.CreateInstance<T>();
            foreach (TableSchema.TableColumnSetting setting in this.columnSettings)
            {
                local.SetColumnValue(setting.ColumnName, setting.CurrentValue);
            }
            return local;
        }

        public void CopyFrom(T copyInstance)
        {
            if (copyInstance == null)
            {
                throw new Exception("Copy instance is null");
            }
            foreach (TableSchema.TableColumnSetting setting in copyInstance.columnSettings)
            {
                this.SetColumnValue(setting.ColumnName, setting.CurrentValue);
            }
        }

        public void CopyFrom(DataRow row)
        {
            foreach (TableSchema.TableColumnSetting setting in this.columnSettings)
            {
                try
                {
                    setting.CurrentValue = row[setting.ColumnName];
                    continue;
                }
                catch
                {
                    continue;
                }
            }
        }

        public void CopyTo(T copyInstance)
        {
            if (copyInstance == null)
            {
                copyInstance = Activator.CreateInstance<T>();
            }
            foreach (TableSchema.TableColumnSetting setting in this.columnSettings)
            {
                copyInstance.SetColumnValue(setting.ColumnName, setting.CurrentValue);
            }
        }

        public void CopyTo(DataTable dataTable)
        {
            DataRow row = dataTable.NewRow();
            foreach (TableSchema.TableColumnSetting setting in this.columnSettings)
            {
                try
                {
                    row[setting.ColumnName] = setting.CurrentValue;
                    continue;
                }
                catch
                {
                    continue;
                }
            }
            dataTable.Rows.Add(row);
        }

        public static IDataReader FetchAll()
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            AbstractRecord<T>.CheckLogicalDelete(q);
            return DataService.GetReader(q.BuildSelectCommand());
        }

        public static IDataReader FetchAll(OrderBy orderBy)
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            q.OrderBy = orderBy;
            AbstractRecord<T>.CheckLogicalDelete(q);
            return DataService.GetReader(q.BuildSelectCommand());
        }

        public static IDataReader FetchByParameter(string columnName, object oValue)
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            q.AddWhere(columnName, oValue);
            AbstractRecord<T>.CheckLogicalDelete(q);
            return DataService.GetReader(q.BuildSelectCommand());
        }

        public static IDataReader FetchByParameter(string columnName, Comparison comparison, object oValue)
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            q.AddWhere(columnName, comparison, oValue);
            AbstractRecord<T>.CheckLogicalDelete(q);
            return DataService.GetReader(q.BuildSelectCommand());
        }

        public static IDataReader FetchByParameter(string columnName, object oValue, OrderBy orderBy)
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            q.OrderBy = orderBy;
            q.AddWhere(columnName, oValue);
            AbstractRecord<T>.CheckLogicalDelete(q);
            return DataService.GetReader(q.BuildSelectCommand());
        }

        public static IDataReader FetchByParameter(string columnName, Comparison comparison, object oValue, OrderBy orderBy)
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            q.AddWhere(columnName, comparison, oValue);
            q.OrderBy = orderBy;
            AbstractRecord<T>.CheckLogicalDelete(q);
            return DataService.GetReader(q.BuildSelectCommand());
        }

        public static IDataReader FetchByQuery(SubSonic.Query query)
        {
            AbstractRecord<T>.CheckLogicalDelete(query);
            return DataService.GetReader(query.BuildSelectCommand());
        }

        public static IDataReader Find(T item)
        {
            return AbstractRecord<T>.Find(item, null);
        }

        public static IDataReader Find(T item, OrderBy orderBy)
        {
            SubSonic.Query q = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            AbstractRecord<T>.CheckLogicalDelete(q);
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                string columnName = column.ColumnName;
                object columnValue = item.GetColumnValue<object>(columnName);
                if (!Utility.IsAuditField(columnName))
                {
                    object empty = string.Empty;
                    switch (column.DataType)
                    {
                        case DbType.Boolean:
                            empty = false;
                            break;

                        case DbType.Currency:
                        case DbType.Decimal:
                        case DbType.Double:
                        case DbType.Int16:
                        case DbType.Int32:
                            empty = 0;
                            break;

                        case DbType.Date:
                        case DbType.DateTime:
                            empty = new DateTime(0x76c, 1, 1);
                            break;

                        case DbType.Guid:
                            empty = Guid.Empty;
                            break;
                    }
                    if ((columnValue != null) && !columnValue.Equals(empty))
                    {
                        q.AddWhere(columnName, columnValue);
                    }
                }
            }
            if (orderBy != null)
            {
                q.OrderBy = orderBy;
            }
            return DataService.GetReader(q.BuildSelectCommand());
        }

        protected void ForceDefaults()
        {
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                if (!string.IsNullOrEmpty(column.DefaultSetting))
                {
                    if (!Utility.IsMatch(column.DefaultSetting, "DEFAULT"))
                    {
                        QueryCommand cmd = new QueryCommand("SELECT " + column.DefaultSetting, column.Table.Provider.Name);
                        this.SetColumnValue(column.ColumnName, DataService.ExecuteScalar(cmd));
                    }
                }
                else
                {
                    this.SetColumnValue(column.ColumnName, Utility.GetDefaultSetting(column));
                }
            }
        }

        public TableSchema.TableColumnSettingCollection GetColumnSettings()
        {
            return this.columnSettings;
        }

        public CT GetColumnValue<CT>(string columnName)
        {
            CT local = default(CT);
            if (this.columnSettings != null)
            {
                local = this.columnSettings.GetValue<CT>(columnName);
            }
            return local;
        }

        public DbType GetDBType(string columnName)
        {
            return AbstractRecord<T>.BaseSchema.GetColumn(columnName).DataType;
        }

        protected static object GetDefaultSetting(TableSchema.TableColumn column)
        {
            return Utility.GetDefaultSetting(column);
        }

        public static ListItemCollection GetListItems()
        {
            return AbstractRecord<T>.GetListItems(AbstractRecord<T>.BaseSchema.Columns[1].ColumnName);
        }

        public static ListItemCollection GetListItems(string textColumn)
        {
            ListItemCollection items = new ListItemCollection();
            string columnName = AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName;
            string str2 = AbstractRecord<T>.BaseSchema.GetColumn(textColumn).ColumnName;
            SubSonic.Query query = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            query.SelectList = columnName + "," + str2;
            query.OrderBy = OrderBy.Asc(str2);
            IDataReader reader = query.ExecuteReader();
            while (reader.Read())
            {
                ListItem item = new ListItem(reader[1].ToString(), reader[0].ToString());
                items.Add(item);
            }
            return items;
        }

        public object GetPrimaryKeyValue()
        {
            return this.columnSettings.GetValue<object>(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName);
        }

        public TableSchema.Table GetSchema()
        {
            return AbstractRecord<T>.BaseSchema;
        }

        public QueryCommand GetSelectCommand()
        {
            SubSonic.Query qry = new SubSonic.Query(AbstractRecord<T>.BaseSchema);
            qry.QueryType = QueryType.Select;
            return DataService.BuildCommand(qry);
        }

        public bool HasErrors()
        {
            return (this.errorList.Count > 0);
        }

        public virtual void Initialize()
        {
        }

        public string Inspect()
        {
            return this.Inspect(true);
        }

        public string Inspect(bool useHtml)
        {
            StringBuilder builder = new StringBuilder();
            if (useHtml)
            {
                builder.Append("<table><tr><td colspan=2><h3>" + AbstractRecord<T>.BaseSchema.Name + " Inspection</h3></td></tr>");
                foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
                {
                    builder.Append(string.Concat(new object[] { "<tr><td><span style=\"font-weight:bold\">", column.ColumnName, "</span></td><td>", this.GetColumnValue<object>(column.ColumnName), "</td></tr>" }));
                }
                builder.Append("</table>");
                return builder.ToString();
            }
            builder.AppendLine("#################" + AbstractRecord<T>.BaseSchema.Name + " Inspection ####################");
            foreach (TableSchema.TableColumn column2 in AbstractRecord<T>.BaseSchema.Columns)
            {
                builder.AppendLine(column2.ColumnName + ": " + this.GetColumnValue<object>(column2.ColumnName));
            }
            builder.AppendLine("#############################################################################");
            return builder.ToString();
        }

        public virtual void Load(DataRow dr)
        {
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                try
                {
                    this.SetColumnValue(column.ColumnName, dr[column.ColumnName]);
                    continue;
                }
                catch (Exception exception)
                {
                    throw new Exception("Unable to set column value for " + column.ColumnName + "; " + exception.Message);
                }
            }
            this.SetLoadState();
        }

        public virtual void Load(DataTable tbl)
        {
            if (tbl.Rows.Count > 0)
            {
                DataRow dr = tbl.Rows[0];
                this.Load(dr);
            }
        }

        public virtual void Load(IDataReader rdr)
        {
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                try
                {
                    this.SetColumnValue(column.ColumnName, rdr[column.ColumnName]);
                    continue;
                }
                catch (Exception exception)
                {
                    throw new Exception("Unable to set column value for " + column.ColumnName + ": " + exception.Message);
                }
            }
            this.SetLoadState();
            this.MarkClean();
        }

        public void LoadAndCloseReader(IDataReader rdr)
        {
            if (rdr.Read())
            {
                this.Load(rdr);
            }
            if (!rdr.IsClosed)
            {
                rdr.Close();
            }
        }

        public void LoadByKey(object keyID)
        {
            this.MarkOld();
            IDataReader rdr = null;
            try
            {
                rdr = new SubSonic.Query(AbstractRecord<T>.BaseSchema).AddWhere(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName, keyID).ExecuteReader();
                if (rdr.Read())
                {
                    this.Load(rdr);
                }
                else
                {
                    this.MarkNew();
                }
            }
            finally
            {
                if (rdr != null)
                {
                    rdr.Close();
                }
            }
        }

        public void LoadByParam(string columnName, object paramValue)
        {
            this.MarkOld();
            IDataReader rdr = null;
            try
            {
                rdr = new SubSonic.Query(AbstractRecord<T>.BaseSchema).AddWhere(columnName, paramValue).ExecuteReader();
                if (rdr.Read())
                {
                    this.Load(rdr);
                }
                else
                {
                    this.MarkNew();
                }
            }
            finally
            {
                if (rdr != null)
                {
                    rdr.Close();
                }
            }
        }

        protected virtual void Loaded()
        {
        }

        public void LoadFromPost()
        {
            this.LoadFromPost(true);
        }

        public void LoadFromPost(bool validatePost)
        {
            if (HttpContext.Current != null)
            {
                NameValueCollection form = HttpContext.Current.Request.Form;
                TableSchema.TableColumnSettingCollection columnSettings = this.GetColumnSettings();
                if ((form != null) && (columnSettings != null))
                {
                    foreach (string str in form.AllKeys)
                    {
                        foreach (TableSchema.TableColumnSetting setting in columnSettings)
                        {
                            if ((str.ToLower().EndsWith("_" + setting.ColumnName.ToLower()) || str.ToLower().EndsWith("$" + setting.ColumnName.ToLower())) || str.ToLower().Equals(setting.ColumnName.ToLower()))
                            {
                                setting.CurrentValue = form[str];
                            }
                        }
                    }
                }
                if (validatePost)
                {
                    this.ValidateColumnSettings();
                    if (this.errorList.Count > 0)
                    {
                        if (HttpContext.Current == null)
                        {
                            throw new Exception("Validation error - catch this and check the ExceptionList property to review the exceptions. You can change the output message as needed by accessing the ExceptionMessage properties of this object");
                        }
                        string str2 = "<b>Validation Error:<b><ul>";
                        foreach (string str3 in this.errorList)
                        {
                            str2 = str2 + "<li><i>" + str3 + "</i></li>";
                        }
                        throw new Exception(str2 + "</ul>");
                    }
                }
            }
        }

        public void MarkClean()
        {
            this.columnSettings.IsDirty = false;
        }

        public void MarkNew()
        {
            this._isNew = true;
        }

        public void MarkOld()
        {
            this._isNew = false;
        }

        public object NewFromXML(string xml)
        {
            object obj2 = null;
            Type type = typeof(T);
            if (xml.Length > 0)
            {
                XmlSerializer serializer = new XmlSerializer(type);
                new StringBuilder().Append(xml);
                StringReader textReader = new StringReader(xml);
                obj2 = serializer.Deserialize(textReader);
                textReader.Close();
            }
            return obj2;
        }

        public static SubSonic.Query Query()
        {
            Activator.CreateInstance<T>();
            return new SubSonic.Query(AbstractRecord<T>.table);
        }

        public void SetColumnValue(string columnName, object oValue)
        {
            if (this.columnSettings == null)
            {
                this.columnSettings = new TableSchema.TableColumnSettingCollection();
            }
            this.columnSettings.SetValue(columnName, oValue);
        }

        protected virtual void SetDefaults()
        {
            bool flag = true;
            bool setPropertyDefaultsFromDatabase = DataService.GetInstance(this.ProviderName).SetPropertyDefaultsFromDatabase;
            if (DataService.GetInstance(this.ProviderName).CurrentSharedConnection != null)
            {
                flag = DataService.GetInstance(this.ProviderName).CurrentSharedConnection.State == ConnectionState.Closed;
            }
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                if ((setPropertyDefaultsFromDatabase && !string.IsNullOrEmpty(column.DefaultSetting)) && flag)
                {
                    if (!Utility.IsMatch(column.DefaultSetting, "DEFAULT"))
                    {
                        QueryCommand cmd = new QueryCommand("SELECT " + column.DefaultSetting, column.Table.Provider.Name);
                        this.SetColumnValue(column.ColumnName, DataService.ExecuteScalar(cmd));
                    }
                }
                else
                {
                    this.SetColumnValue(column.ColumnName, Utility.GetDefaultSetting(column));
                }
            }
            this.Initialize();
        }

        protected void SetLoadState()
        {
            this.IsLoaded = true;
            this.IsNew = false;
            this.Loaded();
        }

        protected void SetPrimaryKey(object oValue)
        {
            this.columnSettings.SetValue(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName, oValue);
        }

        public override string ToString()
        {
            string str = "";
            if (this.columnSettings != null)
            {
                if (this.columnSettings.Count > 1)
                {
                    return this.columnSettings[1].CurrentValue.ToString();
                }
                if (this.columnSettings.Count == 1)
                {
                    str = this.columnSettings[0].CurrentValue.ToString();
                }
            }
            return str;
        }

        public string ToXML()
        {
            string str2;
            Type type = typeof(T);
            XmlSerializer serializer = new XmlSerializer(type);
            using (MemoryStream stream = new MemoryStream())
            {
                serializer.Serialize((Stream) stream, this);
                stream.Position = 0L;
                using (StreamReader reader = new StreamReader(stream))
                {
                    str2 = reader.ReadToEnd();
                }
            }
            return str2;
        }

        public void ValidateColumnSettings()
        {
            foreach (TableSchema.TableColumnSetting setting in this.GetColumnSettings())
            {
                object currentValue = setting.CurrentValue;
                bool flag = (currentValue == null) || (currentValue == DBNull.Value);
                TableSchema.TableColumn column = AbstractRecord<T>.table.GetColumn(setting.ColumnName);
                if (!column.IsReadOnly)
                {
                    string str = Utility.ParseCamelToProper(column.ColumnName);
                    Type propertyType = column.GetPropertyType();
                    if (!column.IsNullable && !flag)
                    {
                        try
                        {
                            if (column.DataType != DbType.Guid)
                            {
                                Convert.ChangeType(currentValue, propertyType);
                            }
                        }
                        catch
                        {
                            if (column.IsNumeric)
                            {
                                this.errorList.Add(string.Format(this.InvalidTypeExceptionMessage, str, "number"));
                            }
                            else if (column.IsDateTime)
                            {
                                this.errorList.Add(string.Format(this.InvalidTypeExceptionMessage, str, "date"));
                            }
                            else
                            {
                                this.errorList.Add(string.Format(this.InvalidTypeExceptionMessage, str, "value"));
                            }
                        }
                    }
                    bool flag2 = Utility.IsAuditField(column.ColumnName) && !string.IsNullOrEmpty(column.DefaultSetting);
                    if ((!column.IsNullable && flag) && !flag2)
                    {
                        this.errorList.Add(string.Format(this.NullExceptionMessage, str));
                    }
                    if ((!flag && (column.MaxLength > 0)) && ((column.DataType != DbType.Boolean) && (currentValue.ToString().Length > column.MaxLength)))
                    {
                        this.errorList.Add(string.Format(this.LengthExceptionMessage, str, column.MaxLength));
                    }
                }
            }
        }

        protected static TableSchema.Table BaseSchema
        {
            get
            {
                if (AbstractRecord<T>.table == null)
                {
                    Activator.CreateInstance<T>();
                }
                return AbstractRecord<T>.table;
            }
            set
            {
                AbstractRecord<T>.table = value;
            }
        }

        [HiddenForDataBinding(true)]
        protected List<string> Errors
        {
            get
            {
                return this.errorList;
            }
        }

        [HiddenForDataBinding(true), XmlIgnore]
        public string InvalidTypeExceptionMessage
        {
            get
            {
                return this.invalidTypeExceptionMessage;
            }
            protected set
            {
                this.invalidTypeExceptionMessage = value;
            }
        }

        [HiddenForDataBinding(true), XmlIgnore]
        public bool IsDirty
        {
            get
            {
                return this.columnSettings.IsDirty;
            }
        }

        [XmlIgnore, HiddenForDataBinding(true)]
        public bool IsLoaded
        {
            get
            {
                return this._isLoaded;
            }
            set
            {
                this._isLoaded = value;
            }
        }

        [HiddenForDataBinding(true), XmlIgnore]
        public bool IsNew
        {
            get
            {
                return this._isNew;
            }
            set
            {
                this._isNew = value;
            }
        }

        protected static bool IsSchemaInitialized
        {
            get
            {
                return (((AbstractRecord<T>.table != null) && (AbstractRecord<T>.table.Columns != null)) && (AbstractRecord<T>.table.Columns.Count > 0));
            }
        }

        [XmlIgnore, HiddenForDataBinding(true)]
        public string LengthExceptionMessage
        {
            get
            {
                return this.lengthExceptionMessage;
            }
            protected set
            {
                this.lengthExceptionMessage = value;
            }
        }

        [HiddenForDataBinding(true), XmlIgnore]
        public string NullExceptionMessage
        {
            get
            {
                return this.nullExceptionMessage;
            }
            protected set
            {
                this.nullExceptionMessage = value;
            }
        }

        [XmlIgnore, HiddenForDataBinding(true)]
        public string ProviderName
        {
            get
            {
                return this._providerName;
            }
            protected set
            {
                this._providerName = value;
            }
        }

        [HiddenForDataBinding(true), XmlIgnore]
        public string TableName
        {
            get
            {
                return AbstractRecord<T>.BaseSchema.TableName;
            }
        }
    }
}

